Skip to content

Add uWebSockets.js — native C++ HTTP engine for Node.js#254

Merged
MDA2AV merged 1 commit intoMDA2AV:mainfrom
BennyFranciscus:add-uwebsockets
Mar 29, 2026
Merged

Add uWebSockets.js — native C++ HTTP engine for Node.js#254
MDA2AV merged 1 commit intoMDA2AV:mainfrom
BennyFranciscus:add-uwebsockets

Conversation

@BennyFranciscus
Copy link
Copy Markdown
Collaborator

@BennyFranciscus BennyFranciscus commented Mar 29, 2026

uWebSockets.js

Engine entry for µWebSockets.js v20.61.0 by Alex Hultman / uNetworking AB.

What is it?

uWebSockets.js is a native C++ HTTP/WebSocket server exposed to Node.js as a V8 addon. It bypasses Node's built-in HTTP stack entirely — the HTTP parsing, event loop, and socket handling all happen in C++ via the µSockets library (epoll-based). It's the engine behind Bun's HTTP server and consistently tops TechEmpower benchmarks.

7k+ stars, established since 2016, fuzz-tested via OSS-Fuzz.

Implementation details

  • Type: engine (native C++ HTTP, not a JS framework)
  • Language: JS (Node.js host)
  • Engine: uWebSockets (µSockets C++ event loop + HTTP parser)
  • Multi-core: Node cluster module — one uWS event loop per worker, SO_REUSEPORT
  • Base image: Ubuntu 24.04 (glibc 2.39 required for prebuilt uWS binaries)
  • Tests: baseline, pipelined, limited-conn

Validation

=== Results: 7 passed, 0 failed ===

All baseline (GET/POST/chunked), anti-cheat randomized, and pipelined tests pass.

Notes

  • Uses res.cork() for batching header+body writes (recommended by uWS docs)
  • res.onAborted() registered before body reading per uWS requirements
  • Query string parsed via req.getQuery() — returns raw query string
  • The prebuilt native addon requires glibc ≥ 2.38, hence Ubuntu 24.04 instead of the typical node:22-slim (Debian bookworm with glibc 2.36)

cc uNetworking — would love to see how uWS.js performs on the HttpArena benchmark hardware!

uWebSockets.js v20.61.0 by Alex Hultman / uNetworking AB.
Written in C++, exposed as a native V8 addon — bypasses Node's HTTP stack entirely.
Uses µSockets event loop with epoll for high-performance HTTP/WebSocket serving.
Multi-core via Node cluster module (SO_REUSEPORT).

Type: engine
Language: JS
Tests: baseline, pipelined, limited-conn
Validation: 7/7 passed
@MDA2AV
Copy link
Copy Markdown
Owner

MDA2AV commented Mar 29, 2026

@alexhultman doesnt exist

@MDA2AV
Copy link
Copy Markdown
Owner

MDA2AV commented Mar 29, 2026

/benchmark -f uwebsockets

@github-actions
Copy link
Copy Markdown
Contributor

🚀 Benchmark run triggered for uwebsockets (all tests). Results will be posted here when done.

@BennyFranciscus
Copy link
Copy Markdown
Collaborator Author

Oh good catch — looks like @alexhultman's GitHub account doesn't exist anymore (or was renamed). The author goes by Alex Hultman and runs the uNetworking org. I'll update the PR description to reference the org instead.

The implementation itself should be fine regardless — happy to adjust anything else if needed!

@github-actions
Copy link
Copy Markdown
Contributor

Benchmark Results

Framework: uwebsockets | Test: all tests

uwebsockets / baseline / 512c (p=1, r=0, cpu=64)
  Best: 2317360 req/s (CPU: 6598.3%, Mem: 847.4MiB) ===

uwebsockets / baseline / 4096c (p=1, r=0, cpu=64)
  Best: 2396442 req/s (CPU: 6518.9%, Mem: 819.0MiB) ===

uwebsockets / baseline / 16384c (p=1, r=0, cpu=64)
  Best: 2283617 req/s (CPU: 6492.7%, Mem: 927.4MiB) ===

uwebsockets / pipelined / 512c (p=16, r=0, cpu=unlimited)
  Best: 18084064 req/s (CPU: 8481.9%, Mem: 1.3GiB) ===

uwebsockets / pipelined / 4096c (p=16, r=0, cpu=unlimited)
  Best: 20086425 req/s (CPU: 9141.7%, Mem: 1.4GiB) ===

uwebsockets / pipelined / 16384c (p=16, r=0, cpu=unlimited)
  Best: 19114586 req/s (CPU: 8653.2%, Mem: 1.5GiB) ===

uwebsockets / limited-conn / 512c (p=1, r=10, cpu=unlimited)
  Best: 1277279 req/s (CPU: 5846.2%, Mem: 1.5GiB) ===

uwebsockets / limited-conn / 4096c (p=1, r=10, cpu=unlimited)
  Best: 1749793 req/s (CPU: 6566.8%, Mem: 1.5GiB) ===

Comparison with main

No results found for uwebsockets

Full log
  Latency samples: 95764080 / 95764080 responses (100.0%)
  CPU: 8653.2% | Mem: 1.5GiB

=== Best: 19114586 req/s (CPU: 8653.2%, Mem: 1.5GiB) ===
[dry-run] Results not saved (use --save to persist)
httparena-bench-uwebsockets
httparena-bench-uwebsockets

==============================================
=== uwebsockets / limited-conn / 512c (p=1, r=10, cpu=unlimited) ===
==============================================
5405cee13a0e9d02c4e4df3fea353ff23d6c1f478666f07dc5543a9948fb04b6
[wait] Waiting for server...
[ready] Server is up

[run 1/3]
gcannon — io_uring HTTP load generator
  Target:    localhost:8080/
  Threads:   64
  Conns:     512 (8/thread)
  Pipeline:  1
  Req/conn:  10
  Templates: 3
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency    382us    224us    848us   2.37ms   4.60ms

  6338160 requests in 5.00s, 6338160 responses
  Throughput: 1.27M req/s
  Bandwidth:  170.40MB/s
  Status codes: 2xx=6338160, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 6338138 / 6338160 responses (100.0%)
  Reconnects: 633822
  Per-template: 2112701,2112766,2112671
  Per-template-ok: 2112701,2112766,2112671
  CPU: 5922.0% | Mem: 1.5GiB

[run 2/3]
gcannon — io_uring HTTP load generator
  Target:    localhost:8080/
  Threads:   64
  Conns:     512 (8/thread)
  Pipeline:  1
  Req/conn:  10
  Templates: 3
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency    378us    222us    846us   2.35ms   4.41ms

  6386402 requests in 5.00s, 6386395 responses
  Throughput: 1.28M req/s
  Bandwidth:  171.68MB/s
  Status codes: 2xx=6386395, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 6386378 / 6386395 responses (100.0%)
  Reconnects: 638659
  Per-template: 2128770,2128771,2128837
  Per-template-ok: 2128770,2128771,2128837
  CPU: 5846.2% | Mem: 1.5GiB

[run 3/3]
gcannon — io_uring HTTP load generator
  Target:    localhost:8080/
  Threads:   64
  Conns:     512 (8/thread)
  Pipeline:  1
  Req/conn:  10
  Templates: 3
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency    393us    216us    875us   2.71ms   5.67ms

  6178915 requests in 5.00s, 6178921 responses
  Throughput: 1.24M req/s
  Bandwidth:  166.10MB/s
  Status codes: 2xx=6178921, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 6178898 / 6178921 responses (100.0%)
  Reconnects: 617887
  Per-template: 2059676,2059527,2059695
  Per-template-ok: 2059676,2059527,2059695
  CPU: 5640.3% | Mem: 1.5GiB

=== Best: 1277279 req/s (CPU: 5846.2%, Mem: 1.5GiB) ===
  Input BW: 98.67MB/s (avg template: 81 bytes)
[dry-run] Results not saved (use --save to persist)
httparena-bench-uwebsockets
httparena-bench-uwebsockets

==============================================
=== uwebsockets / limited-conn / 4096c (p=1, r=10, cpu=unlimited) ===
==============================================
6e6c79d5cb11dd1c75c118b984f20ee948dfb7b0d2f0a54ad061c26c5a4d0bb5
[wait] Waiting for server...
[ready] Server is up

[run 1/3]
gcannon — io_uring HTTP load generator
  Target:    localhost:8080/
  Threads:   64
  Conns:     4096 (64/thread)
  Pipeline:  1
  Req/conn:  10
  Templates: 3
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   2.34ms   1.14ms   5.94ms   14.40ms   27.00ms

  8474829 requests in 5.00s, 8474392 responses
  Throughput: 1.69M req/s
  Bandwidth:  227.70MB/s
  Status codes: 2xx=8474393, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 8474354 / 8474392 responses (100.0%)
  Reconnects: 846783
  Per-template: 2825038,2824787,2824529
  Per-template-ok: 2825038,2824787,2824529

  WARNING: 18446744073709551615/8474392 responses (217676313223527.4%) had unexpected status (expected 2xx)
  CPU: 6368.4% | Mem: 1.5GiB

[run 2/3]
gcannon — io_uring HTTP load generator
  Target:    localhost:8080/
  Threads:   64
  Conns:     4096 (64/thread)
  Pipeline:  1
  Req/conn:  10
  Templates: 3
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   2.27ms   1.10ms   5.44ms   14.40ms   29.80ms

  8751290 requests in 5.00s, 8748968 responses
  Throughput: 1.75M req/s
  Bandwidth:  235.08MB/s
  Status codes: 2xx=8748968, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 8748948 / 8748968 responses (100.0%)
  Reconnects: 873432
  Per-template: 2916356,2916281,2916311
  Per-template-ok: 2916356,2916281,2916311
  CPU: 6566.8% | Mem: 1.5GiB

[run 3/3]
gcannon — io_uring HTTP load generator
  Target:    localhost:8080/
  Threads:   64
  Conns:     4096 (64/thread)
  Pipeline:  1
  Req/conn:  10
  Templates: 3
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   2.30ms   1.11ms   5.60ms   14.20ms   29.30ms

  8648585 requests in 5.00s, 8647564 responses
  Throughput: 1.73M req/s
  Bandwidth:  232.45MB/s
  Status codes: 2xx=8647564, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 8647545 / 8647564 responses (100.0%)
  Reconnects: 863894
  Per-template: 2882562,2882579,2882404
  Per-template-ok: 2882562,2882579,2882404
  CPU: 6348.6% | Mem: 1.5GiB

=== Best: 1749793 req/s (CPU: 6566.8%, Mem: 1.5GiB) ===
  Input BW: 135.17MB/s (avg template: 81 bytes)
[dry-run] Results not saved (use --save to persist)
httparena-bench-uwebsockets
httparena-bench-uwebsockets
[skip] uwebsockets does not subscribe to json
[skip] uwebsockets does not subscribe to upload
[skip] uwebsockets does not subscribe to compression
[skip] uwebsockets does not subscribe to noisy
[skip] uwebsockets does not subscribe to mixed
[skip] uwebsockets does not subscribe to static
[skip] uwebsockets does not subscribe to async-db
[skip] uwebsockets does not subscribe to baseline-h2
[skip] uwebsockets does not subscribe to static-h2
[skip] uwebsockets does not subscribe to baseline-h3
[skip] uwebsockets does not subscribe to static-h3
[skip] uwebsockets does not subscribe to unary-grpc
[skip] uwebsockets does not subscribe to unary-grpc-tls
[skip] uwebsockets does not subscribe to echo-ws
[restore] Restoring CPU governor to performance...

@BennyFranciscus
Copy link
Copy Markdown
Collaborator Author

Benchmark Analysis

These numbers are wild 🔥

Profile Connections req/s CPU Memory
baseline 512c 2.32M 6598% 847 MiB
baseline 4096c 2.40M 6519% 819 MiB
baseline 16384c 2.28M 6493% 927 MiB
pipelined 512c 18.1M 8482% 1.3 GiB
pipelined 4096c 20.1M 9142% 1.4 GiB
pipelined 16384c 19.1M 8653% 1.5 GiB
limited-conn 512c 1.28M 5846% 1.5 GiB
limited-conn 4096c 1.75M 6567% 1.5 GiB

20M pipelined — that puts it right at the top of the leaderboard. Zero 5xx across all profiles, clean run.

One note: the limited-conn 4096c run 1 has a gcannon counter underflow warning (18446744073709551615 responses = uint64 max). The actual status codes are fine (all 2xx), so it's just a display bug in gcannon's counter arithmetic, not a real issue.

Only subscribed to baseline/pipelined/limited-conn right now — could expand to more tests later if there's interest.

@MDA2AV
Copy link
Copy Markdown
Owner

MDA2AV commented Mar 29, 2026

/benchmark -f uwebsockets --save

@github-actions
Copy link
Copy Markdown
Contributor

🚀 Benchmark run triggered for uwebsockets (all tests) with --save. Results will be posted here when done.

@MDA2AV MDA2AV merged commit a76d067 into MDA2AV:main Mar 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants